#include "semaphore.h"
#include "sched.h"

semaphore sems[NSEM];
bool sem_used[NSEM] = {0};

// 
// create new semaphore,
// return the semaphore id([0, NSEM]),
// or -1 if there are no more semaphores.
// （注意信号灯的初值必须非负。）
// 
int do_sem_new(int value) {
    if (value < 0) return -1;

    for (int i = 0; i < NSEM; i++) {
        if (!sem_used[i]) {
            sem_used[i] = TRUE;
            sems[i].value = value;
            sems[i].wq_head = sems[i].wq_tail = NULL;
            return i;
        }
    }
    return -1; // no free semaphores
}

// 
// 信号灯P操作，将信号灯的值减一，
// 如果信号灯的值小于零，则当前进程将睡眠，并加入等待队列。
// 然后转进程调度。
// 
sem_status do_sem_P(int sem_id) {
    // 如果信号灯序号超出范围或信号灯没有在使用则返回错误
    if (sem_id < 0 || sem_id >= NSEM || !sem_used[sem_id]) {
        return SEM_ERR;
    }
    // 减少信号灯的值
    semaphore *sem = &sems[sem_id];
    sem->value--;
    // 如果信号灯的值小于0，则当前进程将睡眠，并加入等待队列
    if (sem->value < 0) {
        if (sem->wq_tail == NULL) {
            sem->wq_head = sem->wq_tail = current;
        } else {
            sem->wq_tail->queue_next = current;
            sem->wq_tail = current;
        }
        current->status = BLOCKED;
        schedule();
    }
    return SEM_OK;
}

// 
// 信号灯V操作，将信号灯的值加一，
// 如果信号灯的值小于或等于0，则需要唤醒等待队列中的一个进程，置为就绪状态
// 然后转进程调度。
// 
sem_status do_sem_V(int sem_id) {
    // 如果信号灯序号超出范围或信号灯没有在使用则返回错误
    if (sem_id < 0 || sem_id >= NSEM || !sem_used[sem_id]) {
        return SEM_ERR;
    }
    // 增加信号灯的值
    semaphore* sem = &sems[sem_id];
    sem->value++;
    // 如果信号灯的值小于或等于0，则需要唤醒等待队列中的一个进程，置为就绪状态
    if (sem->value <= 0) {
        process *p = sem->wq_head;
        if (NULL == p) {
            return SEM_ERR;
        }
        sem->wq_head = p->queue_next;
        if (sem->wq_head == NULL) {
            sem->wq_tail = NULL;
        }
        p->status = READY;
        p->queue_next = NULL;
        schedule();
    }
    return SEM_OK;
}

//
// 释放信号灯
//
sem_status do_sem_free(int sem_id) {
    // 如果信号灯序号超出范围或信号灯没有在使用则返回错误
    if (sem_id < 0 || sem_id >= NSEM || !sem_used[sem_id]) {
        return SEM_ERR;
    }
    // 释放信号灯
    sem_used[sem_id] = FALSE;
    // 释放信号灯时，如果等待队列中有进程，则唤醒进程
    semaphore* sem = &sems[sem_id];
    if (sem->value < 0) {
        while (sem->wq_head != NULL){
            process *p = sem->wq_head;
            sem->wq_head = p->queue_next;
            if (sem->wq_head == NULL) {
                sem->wq_tail = NULL;
            }
            p->status = READY;
        } 
        schedule();
    }
    return SEM_OK;
}
